package com.proj.android.canyondefense.core.gl;

import java.util.ArrayList;
import java.util.List;

import com.proj.android.canyondefense.core.base.GameObject;

import android.annotation.SuppressLint;
import android.util.FloatMath;

public class SpatialHashGrid {
    private List<GameObject>[] _dynamicCells;
    private List<GameObject>[] _staticCells;
    private int _cellsPerRow;
    private int _cellsPerCol;
    private float _cellSize;
    private int[] _cellIds = new int[4];
    private List<GameObject> foundObjects;
    
    @SuppressWarnings("unchecked")
	@SuppressLint("FloatMath")
    public SpatialHashGrid(float worldWidth, float worldHeight, float cellSize) {
        this._cellSize = cellSize;
        this._cellsPerRow = (int)FloatMath.ceil(worldWidth / cellSize);
        this._cellsPerCol = (int)FloatMath.ceil(worldHeight / cellSize);
        
        int numCells = _cellsPerRow * _cellsPerCol;
        
        this._dynamicCells = new List[numCells];
        this._staticCells = new List[numCells];
        
        for(int i = 0; i < numCells; i++) {
            _dynamicCells[i] = new ArrayList<GameObject>(10);
            _staticCells[i] = new ArrayList<GameObject>(10);
        }
        foundObjects = new ArrayList<GameObject>(10);
    }
    
    public void insertStaticObject(GameObject obj) {
        int[] cellIds = getCellIds(obj);
        int i = 0;
        int cellId = -1;
        while(i <= 3 && (cellId = cellIds[i++]) != -1) {
            _staticCells[cellId].add(obj);
        }
    }
    
    public void insertDynamicObject(GameObject obj) {
        int[] cellIds = getCellIds(obj);
        int i = 0;
        int cellId = -1;
        while(i <= 3 && (cellId = cellIds[i++]) != -1) {
            _dynamicCells[cellId].add(obj);
        }
    }
    
    public void removeObject(GameObject obj) {
        int[] cellIds = getCellIds(obj);
        int i = 0;
        int cellId = -1;
        while(i <= 3 && (cellId = cellIds[i++]) != -1) {
            _dynamicCells[cellId].remove(obj);
            _staticCells[cellId].remove(obj);
        }
    }
    
    public void clearDynamicCells(GameObject obj) {
        int len = _dynamicCells.length;
        for(int i = 0; i < len; i++) {
            _dynamicCells[i].clear();
        }
    }
    
    public List<GameObject> getPotentialColliders(GameObject obj) {
        foundObjects.clear();
        int[] cellIds = getCellIds(obj);
        int i = 0;
        int cellId = -1;
        while(i <= 3 && (cellId = cellIds[i++]) != -1) {
            int len = _dynamicCells[cellId].size();
            for(int j = 0; j < len; j++) {
                GameObject collider = _dynamicCells[cellId].get(j);
                if(!foundObjects.contains(collider))
                    foundObjects.add(collider);
            }
            
            len = _staticCells[cellId].size();
            for(int j = 0; j < len; j++) {
                GameObject collider = _staticCells[cellId].get(j);
                if(!foundObjects.contains(collider))
                    foundObjects.add(collider);
            }
        }
        return foundObjects;
    }
    
    @SuppressLint("FloatMath")
	public int[] getCellIds(GameObject obj) {
        int x1 = (int)FloatMath.floor(obj.bounds.pos.x / _cellSize);
        int y1 = (int)FloatMath.floor(obj.bounds.pos.y / _cellSize);
        int x2 = (int)FloatMath.floor((obj.bounds.pos.x + obj.bounds.width) / _cellSize);
        int y2 = (int)FloatMath.floor((obj.bounds.pos.y + obj.bounds.height) / _cellSize);
        
        if(x1 == x2 && y1 == y2) {
            if(x1 >= 0 && x1 < _cellsPerRow && y1 >= 0 && y1 < _cellsPerCol) {
                _cellIds[0] = x1 + y1 * _cellsPerRow;
            } else {
                _cellIds[0] = -1;
            }
            _cellIds[1] = -1;
            _cellIds[2] = -1;
            _cellIds[3] = -1;
        } else if(x1 == x2) {
            int i = 0;
            if(x1 >= 0 && x1 < _cellsPerRow) {
                if(y1 >= 0 && y1 < _cellsPerCol) {
                    _cellIds[i++] = x1 + y1 * _cellsPerRow;
                }
                if(y2 >= 0 && y2 < _cellsPerCol) {
                    _cellIds[i++] = x1 + y2 * _cellsPerRow;
                }
            }
            while(i <= 3) {
            	_cellIds[i++] = -1;
            }
        } else if(y1 == y2) {
            int i = 0;
            if(y1 >= 0 && y1 < _cellsPerCol) {
                if(x1 >= 0 && x1 < _cellsPerRow) {
                    _cellIds[i++] = x1 + y1 * _cellsPerRow;
                }
                if(x2 >= 0 && x2 < _cellsPerRow) {
                    _cellIds[i++] = x2 + y1 * _cellsPerRow;
                }
            }
            while(i <= 3) {
            	_cellIds[i++] = -1;                       
            }
        } else {
            int i = 0;
            int y1CellsPerRow = y1 * _cellsPerRow;
            int y2CellsPerRow = y2 * _cellsPerRow;
            if(x1 >= 0 && x1 < _cellsPerRow && y1 >= 0 && y1 < _cellsPerCol) {
                _cellIds[i++] = x1 + y1CellsPerRow;
            }
            if(x2 >= 0 && x2 < _cellsPerRow && y1 >= 0 && y1 < _cellsPerCol) {
                _cellIds[i++] = x2 + y1CellsPerRow;
            }
            if(x2 >= 0 && x2 < _cellsPerRow && y2 >= 0 && y2 < _cellsPerCol) {
                _cellIds[i++] = x2 + y2CellsPerRow;
            }
            if(x1 >= 0 && x1 < _cellsPerRow && y2 >= 0 && y2 < _cellsPerCol) {
                _cellIds[i++] = x1 + y2CellsPerRow;
            }
            while(i <= 3) {
            	_cellIds[i++] = -1;
            }
        }
        return _cellIds;
    }
}